# 35. re模块 - 正则匹配模块


# re模块

用于使用正则表达式来匹配字符串的内容

# 匹配

# findall方法

findall:匹配所有能匹配上的字符

返回值类型:列表

使用格式:re.findall("正则表达式","要过滤的字符串") 或 变量 = re.findall("正则表达式","要过滤的字符串")

import re
sre = re.findall("\d+","1213skdjalkl1231nkl1231klnlkjh31")
print(sre)

执行结果:
['1213', '1231', '1231', '31']

如果匹配没有,则返回空列表

# search方法

search:匹配第一个结果,简单按规定的正则匹配,匹配到就马上返回结果,不会在匹配接下来的字符串

返回值类型:正则匹配的对象

使用格式:re.search("正则表达式","要过滤的字符串") 或 变量 = re.search("正则表达式","要过滤的字符串")

import re
sre = re.search("\d+","1213skdjalkl1231nkl1231klnlkjh31")
print(type(sre))
print(sre)

执行结果:
<class '_sre.SRE_Match'>
<_sre.SRE_Match object; span=(0, 4), match='1213'>

如果匹配没有,则返回None

# group方法

使用group方法来查看search方法获取到的取

返回值类型:字符串类型

使用格式:变量.group()

import re
sre = re.search("\d+","1213skdjalkl1231nkl1231klnlkjh31")
print(sre.group())

执行结果:
1213

如果查询search方法获取的取为None,那么会报错

通过if判断,来避免程序报错停止

import re
sre = re.search("\d+","1213skdjalkl1231nkl1231klnlkjh31")
if sre :print(sre.group())
    
执行结果:
1213

# match方法

match:使用方式跟search方法差不多,区别就是:match会在输入的正则表达的开头加个^,代表就是使用match只是获取第一个结果并这个结果是要在开头就有的,如果没有则没

返回值类型:正则匹配的对象

使用格式:re.match("正则表达式","要过滤的字符串") 或 变量 = re.match("正则表达式","要过滤的字符串")

import re
sre = re.match("\d+","1213skdjalkl1231nkl1231klnlkjh31")
print(type(sre))
print(sre)

执行结果:
<class '_sre.SRE_Match'>
<_sre.SRE_Match object; span=(0, 4), match='1213'>

如果匹配没有,则返回None

这里见证match的特别之处

import re
sre = re.match("\d+","s1213skdjalkl1231nkl1231klnlkjh31")
print(sre)

执行结果:
None

那为什么结果是None,因为match方法会默认跟写好的正则开头加上^ ,这样子我们的正则就相当变成这样:^\d+

# group方法

使用group方法来查看match方法获取到的取

返回值类型:字符串类型

使用格式:变量.group()

import re
sre = re.match("\d+","1213skdjalkl1231nkl1231klnlkjh31")
print(sre.group())

执行结果:
1213

如果查询search方法获取的取为None,那么会报错

通过if判断,来避免程序报错停止

import re
sre = re.match("\d+","1213skdjalkl1231nkl1231klnlkjh31")
if sre :print(sre.group())
    
执行结果:
1213

# 替换

# sub方法

sub方法,按正则规则匹配出来的值进行替换处理

返回值类型:字符串类型

格式:re.sub("正则规则","要替换的值","要处理的字符串",要替换的次数)

注意:sub替换次数参数默认是替换全部,如果要替换几次可以指定

res = re.sub("\d","S","asjdkl12131klasklf1231sfq12k12kh312h3j1h2k3")
print(type(res))
print(res)

执行结果:
<class 'str'>
asjdklSSSSSklasklfSSSSsfqSSkSSkhSSShSjShSkS

如果正则匹配没有,那么会输出原先的字符串

指定替换次数

import re
res = re.sub("\d","S","asjdkl12131klasklf1231sfq12k12kh312h3j1h2k3",2)
print(res)

执行结果:
asjdklSS131klasklf1231sfq12k12kh312h3j1h2k3

# subn方法

subn方法使用方式跟sub差不多,只是返回值有点不同,返回的有替换后的值还有替换的次数

返回值类型:元组类型

格式:re.subn("正则规则","要替换的值","要处理的字符串",要替换的次数)

注意:subn替换次数参数默认是替换全部,如果要替换几次可以指定

import re
res = re.subn("\d","S","asjdkl12131klasklf1231sfq12k12kh312h3j1h2k3")
print(type(res))
print(res)

执行结果:
<class 'tuple'>
('asjdklSSSSSklasklfSSSSsfqSSkSSkhSSShSjShSkS', 20)

如果正则匹配没有,那么会输出原先的字符串

指定替换次数

import re
res = re.subn("\d","S","asjdkl12131klasklf1231sfq12k12kh312h3j1h2k3",2)
print(res)

执行结果:
('asjdklSS131klasklf1231sfq12k12kh312h3j1h2k3', 2)

# 切割

# split方法

可以通过split按指定的正则规则来切割分隔开来

返回值类型:列表类型

格式:re.split("正则规则","字符串")

import re
res = re.split("\d","江凡22李城18万须21")
print(type(res))
print(res)

执行结果:
<class 'list'>
['江凡', '', '李城', '', '万须', '', '']

# 进阶

# 预编译功能-compile

如果大量使用相同的正则来过滤字符串,那么每一次过滤都要编译一次正则代码,这样会大大的消耗内存空间跟时间

这样子就可以使用预编译功能方法-compile

格式:变量 = re.compile("正则规则")

res = re.compile("^[1-9]\d{16}[0-9x]|^[1-9]\d{14}")
rew = res.findall("123456789012345678")
ree = res.search("876543210987654321")
print(rew)
print(ree.group())

执行结果:
['123456789012345678']
876543210987654321

# 迭代器-finditer

将匹配出来的可以用迭代器来循环输出,输出的值是一个对象,需要加了gtoup方法来取值

一般用于处理大量的字符串数据

格式:re.finditer("正则规则","要匹配的字符串")

import re
res = re.finditer("\d","sahd12h367897894564h12h31jg657683hj1g3h21313g5575671hj3g25454361hj2g1h312354jg3hj1g3hj")
for i in res:
    print(i)
    
执行结果:
<_sre.SRE_Match object; span=(4, 5), match='1'>
<_sre.SRE_Match object; span=(5, 6), match='2'>
<_sre.SRE_Match object; span=(7, 8), match='3'>
<_sre.SRE_Match object; span=(8, 9), match='6'>
.......

# group方法

使用group方法来查看finditer迭代后的对象的结果

import re
res = re.finditer("\d","sahd12h367897894564h12h31jg657683hj1g3h21313g5575671hj3g25454361hj2g1h312354jg3hj1g3hj")
for i in res:
    print(i.group())
    
执行结果:
1
2
3
6
......

# 当re模块遇上正则分组

# 当findall方法遇上分组

到findall方法中的正则有分组 () 时,那么findall方法就会以分组优先

分组优先:只显示分组中的内容

res = re.findall("www.baidu.cn|www.linux91.cn","www.linux91.cn")
ree = re.findall("www\.(baidu|linux91)\.cn","www.linux91.cn")
print(res)
print(ree)

执行结果:
['www.linux91.cn']
['linux91']

分组优先是可以取消的,取消分组优先需要要在正则中的分组里加上 ?: 这二个符号(?😃

res = re.findall("www.baidu.cn|www.linux91.cn","www.linux91.cn")
ree = re.findall("www\.(?:baidu|linux91)\.cn","www.linux91.cn")
print(res)
print(ree)

执行结果:
['www.linux91.cn']
['www.linux91.cn']

就算使用compile对正则进行预编译也一样,需要加上?:

res = re.compile("www\.(baidu|linux91)\.cn")
rew = re.compile("www\.(?:baidu|linux91)\.cn")
res = res.findall("www.linux91.cn")
rew = rew.findall("www.linux91.cn")
print(res)
print(rew)

执行结果:
['linux91']
['www.linux91.cn']

# 当search方法遇上分组

先不说,先看一段代码

rew = re.search('\d+(.\d+)(.\d+)(.\d+)?','1.2.3.4-2*(60+(-40.35/5)-(-4*3))')
print(rew.group())
print(rew.group(0))
print(rew.group(1))
print(rew.group(2))
print(rew.group(3))

执行结果:
1.2.3.4
1.2.3.4
.2
.3
.4

有没有发现一些规律,简单来说,当search方法遇到分组的作用,不是像findall方法一样,自动触发,而是需要手动触发,触方式:在于group方法

  1. group()或group(0):默认显示全部分组内容
  2. group(1):显示第一个分组的内容
  3. group(2):显示第二个分组的内容
  4. group(3):显示第三个分组的内容
  5. group(4):显示第四个分组的内容
  6. ......等等

# 当split方法遇上分组

就直接简单说,当split中的正则有分组,那么就会显示分组中切割消失的值

rew = re.split("\d+","江凡22李城18万须21")
res = re.split("(\d+)","江凡22李城18万须21")
print(rew)
print(res)

执行结果:
['江凡', '李城', '万须', '']
['江凡', '22', '李城', '18', '万须', '21', '']

# 分组练习题

# 去除匹配的值中不要值

可以利用findall方法中的分组优先来去除匹配后的值中不想要的值

字符串:1-2*(60+(-40.35/5)-(-4*3))

匹配字符串中的所有整数跟小数,并去除小数

import re
rew = re.findall("\d+(?:\.\d+)|(\d+)","1-2*(60+(-40.35/5)-(-4*3))")
rew.remove("")
print(rew)

执行结果:
['1', '2', '60', '5', '4', '3']

# 分别获取指定的指推荐使用-search方法

字符串:"<a>abc</a>"

匹配字符串中的<>中的a

匹配字符串中的二个<>中间的值:abc

# 使用findall方法

rew = re.findall("<(\w+)>","<a>abc</a>")
print(rew)
ree = re.findall(">(\w+)<","<a>abc</a>")
print(ree)

执行结果:
['a']
['abc']

# 使用search方法

rew = re.search("<(\w+)>(\w+)</(\w+)>","<a>abc</a>")
print(rew.group(1))
print(rew.group(2))

执行结果:
a
abc

# 分组命名

分组命令,可以把一个分组进行命名,在正则中另一处使用

比如:<h1>ksjakldf</h1>,匹配h1中的所有数据,那要怎么确实<h1>的结尾是<\h1>,难道正则要这样写"<h1>(\w+)<\h1>",这样子不行,因为这样子正则规则就限制在只能匹配<h1>的内容,所以要使用分组命名来保证

分组命名分为二种:分组命名,分组索引

# 分组命名

findall方法

## 没值
rew = re.findall('<(?P<mos>\w+)>(\w+)</(?P=mos)>','<a>wahaha</b>')
print(rew)

## 正确
rew = re.findall('<(?P<mos>\w+)>(\w+)</(?P=mos)>','<a>wahaha</a>')
print(rew)

执行结果:
[('a', 'wahaha')]

为什么会没值,因为如果正则中有命名,并有使用命令,Python会将这二个位置的值进行对比,如果相同则执行下去,如果不相同则取消执行

search方法

## 正常操作
rew = re.search('<(?P<mos>\w+)>(\w+)</(?P=mos)>','<a>wahaha</a>')
print(rew.group())

执行结果:
<a>wahaha</a>


## 指定分组命名显值
rew = re.search('<(?P<mos>\w+)>(\w+)</(?P=mos)>','<a>wahaha</a>')
print(rew.group("mos"))

执行结果:
a

# 索引分组

索引分组按索引来调用分组,从1开始算

rew = re.search('<(\w+)>(\w+)</\\1>','<a>wahaha</a>')
print(rew.group())

执行结果:
<a>wahaha</a>

# re模块中所有方法的flags选项

flags选项这个选项在re中的所有方法都有这个选项

一共有六个用法,博主分为二部分,常用跟不常用

用法

re.findall("正则","字符串",re.S)
## 所有方法使用flags选项,类似以上findall方法的用法

# 常用

常用用法分为三种

  1. re.I:忽略大小写,完整的写法:IGNORECASE
  2. re.M:忽略换行符,多行模式,改变^跟$的行为,完整的写法:MULTILINE
  3. re.S:让 . 字符可以匹配任意字符,包括换行符,完整的写法:DOTALL

# 不常用

不常用用法也分为三种

  1. re.L:做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用,完整的写法:LOCALE
  2. re.U:使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag,完整的写法:UNICODE
  3. re.X:冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释,完整的写法:VERBOSE